/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Element;


import com.netscenery.util.ErrTag;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class Enumeration extends Type {
	
	private ArrayList<Enum> enums;
	private int curHighestValue;
	
	public Enumeration(){

	}
	/**
	 *  ���ڼ̳�ĳ��ö������
	 * @param enu
	 */
	public boolean derived(Enumeration enu){
		if (null == enu){
			return false;
		}
		
		if (null != this.enums){
			return false;
		}
		
		enums.addAll(enu.enums);
		curHighestValue = enu.curHighestValue;
		isDerivedType = true;
		return true;
		
	}
	public int getEnumActualValue(String EnumName){
		if (null == EnumName){
			return -1;
		}
		int curHighValue = 0;
		boolean first = false;
		if (null != enums){
			int size = enums.size();
			for (int i = 0; i < size; i++){
				Enum e = enums.get(i);
				if (null != e ){
					if (false == first){
						first = true;
						if (e.isUseDefaultValue()){
							curHighValue = 0;
						}
						else {
							curHighValue = e.getValue();
						}
					}
					else {
						if (e.isUseDefaultValue()){							
							curHighValue++;						
						}
						else {
							if (e.getValue() > curHighValue){
								curHighValue = e.getValue();
							}
						}
					}
					
					
					if (EnumName.equals(e.getName())){
						if(e.isUseDefaultValue()){
							return curHighValue;
						}
						else {
							return e.getValue();
						}
					}
				}
			}
		}
		return -1;
	}
	/**
	 * ��Enums�����һ��ö��
	 * @param en
	 */
	public boolean addEnum(Enum en){
		//�̳����Ͳ����ٶ���ö��
		if (this.isDerivedType == true){
			return false;
		}
		if (null == en){
			return false;
		}
		//ö����Ʋ����ظ�
		if (null != getEnum(en.getName())){
			return false;
		}
		//ö��ֵ�����ظ�
		if ( !en.isUseDefaultValue()){
			if (null != enums){
				int size = enums.size();
				for (int i = 0; i < size; i++){
					Enum e = enums.get(i);
					if (null != e ){
						//���ܺ�����ö���Ѿ������ʵ�ʵ�valueһ��
						if (en.getValue() == getEnumActualValue(e.getName())){
							return false;
						}
					}
				}
			}
		}
		
		
		
		//���ǰ����λ����2147483647�����������Ĭ��value��EnumԪ��
		if(2147483647 == this.curHighestValue){
			if (true == en.isUseDefaultValue()){
				return false;
			}
		}
		
		if (null == this.enums){
			this.enums = new ArrayList<Enum>(1);
		}
		/*���ǰenums��û�ж����κ�ö�٣������ʹ��Ĭ��ֵ����ʵ��λ��Ϊ0*/
		if (true == en.isUseDefaultValue()){
			if (0 == enums.size()){
				this.curHighestValue = 0;
			}
			else {
				this.curHighestValue++;
			}	
			//en.setActualValue(this.curHighestValue);
		}
		else {
			if (en.getValue() > this.curHighestValue){
				this.curHighestValue = en.getValue();
			}
			//en.setActualValue(en.getValue());
		}


		enums.add(en);

		return true;
	}
	/**
	 * ��Enums�����ָ����Ƶ�ö�٣�λ��ΪĬ��λ��
	 * @param enumName
	 */
	public boolean addEnum(String enumName){

		if (null == enumName){
			return false;
		}
		
		
		Enum  enu = new Enum(enumName);
		addEnum(enu);
		return true;
	}
	/**
	 * ��Enums�����ָ����ơ�ָ��ֵ��ö��
	 * @param enumName
	 * @param value
	 */
	public boolean addEnum(String enumName,int value){
		if (null == enumName){
			return false;
		}
		Enum enu = new Enum(enumName,value);
		
		addEnum(enu);
		return true;
	}
	/**
	 * ��ȡĿǰEnums�д��ڶ����Ѷ����ö��
	 * @return
	 */
	public int getEnumsNumber(){
		if (null == this.enums){
			return 0;
		}
		return this.enums.size();
	}
	/**
	 * ��������ȡö����Ϣ
	 * @param index
	 * @return
	 */
	public Enum getEnum(int index){
		if (null == this.enums){
			return null;
		}
		
		if (index >= this.enums.size()){
			return null;
		}
		return this.enums.get(index);

	}
	/**
	 * ���ö�ٵ���Ƶõ�ö�ٵ���Ϣ
	 * @param enumName
	 * @return
	 */
	public Enum getEnum(String enumName){
		if (null == this.enums){
			return null;
		}
		for (int i = 0; i < this.enums.size();i++){
			Enum en = this.enums.get(i);
			if (null != en){
				if (enumName.equals(en.name)){
					return en;
				}
			}
		}
		
		
		return null;
	}
	/**
	 * ɾ��ָ����ö�٣������ظ�ö�ٵ���Ϣ
	 * @param enumName
	 */
	public Enum removeEnum(String enumName){
		if(null == this.enums){
			return null;
		}
		
		int size = this.enums.size();
		for (int i = 0; i < size; i++){
			Enum en = getEnum(i);
			if (null != en){
				if (enumName.equals(en.name)){
					return this.enums.remove(i);
				}
			}
		}
		
		return null;
	}
	public Enum removeEnum(int index){
		if(null == this.enums){
			return null;
		}
		
		return enums.remove(index);
		

	}
	/**
	 * ���ö���б���û��ָ����Ƶ�ö�٣��򷵻�-1�����򷵻�����
	 * @param enName
	 * @return
	 */
	public int getEnumIndex(String enName){
		if(null == this.enums){
			return -1;
		}
		
		int size = this.enums.size();
		for (int i = 0; i < size; i++){
			Enum en = getEnum(i);
			if (null != en){
				if (enName.equals(en.name)){
					return i;
				}
			}
		}
		
		return -1;
	}
	/**
	 * ��ȡָ��λ����ʵ��λ�ã�����б���û��ָ����λ���򷵻�-1
	 * @param bitName
	 * @return
	 */
//	public int getChildActualPosition(int index){
//		Enum bit = getChild(index);
//		if (null == bit){
//			return -1;
//		}
//		return bit.getActualPostion();
//	}
	
	/**
	 * ��ȡָ��λ����ʵ��λ�ã�����б���û��ָ����λ���򷵻�-1
	 * @param bitName
	 * @return
	 */
//	public int getChildActualPosition(String bitName){
//		int index = getChildIndex(bitName);
//		if (-1 == index){
//			return -1;
//		}
//		
//		return getChildActualPosition(index);
//		
//
//	}
//	public boolean exchangeChildren(int index1, int index2){
//		Enum bit1 = getChild(index1);
//		Enum bit2 = getChild(index2);
//		
//		if (null == bit1 || null == bit2){
//			return false;
//		}
//		
//		this.enums.set(index1, bit2);
//		this.enums.set(index2, bit1);
//		int actualPos1 = bit1.getActualPostion();
//		int actualPos2 = bit2.getActualPostion();
//		//˫������Ĭ�ϵģ�ֻҪ����˫����ʵ��λ�ü���
//		if ((bit1.isUsedefaultpos() == true )
//				&&(bit2.isUsedefaultpos() == true)){
//			bit1.setActualPostion(actualPos2);
//			bit2.setActualPostion(actualPos1);
//		}
//		//˫��������Ĭ�ϵģ���ʲô����Ҫ����
//		else if ((bit1.isUsedefaultpos() == false )
//				&&(bit2.isUsedefaultpos() == false)){
//			
//		}
//		//�����Ҫ��С��index��ʼ���¼���ʵ��λ��
//		else{
//			//TODO:��ʵ��
//		}
//		return true;
//	}




	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();

		if (this.isDerivedType()){
			if (0 != this.getEnumsNumber() ){
				validator.setIsValid(false);
				validator.addMsg(new Message(this,ErrTag.ERROR,"a enumeration type can not be restricted."));

			}
		}
		else {
			if (0 == this.getEnumsNumber() ){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR," a builtin-type enumeration must has at least one enum."));

			}
		}
		
		int size = this.getEnumsNumber();
		for (int i = 0; i < size; i++){
			Enum en = getEnum(i);
			if (null == en){
				continue;
			}
			Validator bValidator = en.validate();
			validator.copy(bValidator);
			
		}
		
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		return validator;
		
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toYang()
	 */
	@Override
	public YangStatement toYang() {
		YangStatement statement = new YangStatement(YangStatement.YANG_STATEMENT_TYPE,this.name);
		
		if (0 != this.getEnumsNumber()){
			for (int i = 0; i < this.getEnumsNumber();i++){
				Enum enu = getEnum(i);
				if (null != enu){
					YangStatement enumStatement = enu.writeYang();
					statement.addChild(enumStatement);
				}
			}
		}
		return statement;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toYang()
	 */
	@Override
	public Element toYin() {
		Element statement = new Element(YangKeyword.TYPE.getKeyword(),env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		statement.setAttribute(name);
		if (0 != this.getEnumsNumber()){
			for (int i = 0; i < this.getEnumsNumber();i++){
				Enum enu = getEnum(i);
				if (null != enu){
					Element enumStatement = enu.writeYin();
					statement.addContent(enumStatement);
				}
			}
		}
		return statement;
		
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toXML()
	 */
	@Override
	public Element toXML() {
		Element element = new Element(XMLTag.XML_TAG_TYPE);
		Element type;
		if (this.isDerivedType() == true){
			type = new Element(	XMLTag.XML_TAG_DERIVEDTYPE);
		}
		else {
			type = new Element(XMLTag.XML_TAG_BASETYPE);
		}
		
		type.setText(this.name);
		element.addContent(type);
		if (0 != this.getEnumsNumber()){
			for (int i = 0; i < this.getEnumsNumber();i++){
				Enum enu = getEnum(i);
				if (null != enu){
					enu.setActualValue(getEnumActualValue(enu.getName()));
					Element enElement = enu.writeXML();
					element.addContent(enElement);
				}
			}
		}
		return element;
	}
	

	public int readXML(Element element) throws ModelException {
		if (null == element){
			return ModelElement.MODELELEMENT_FAIL;
		}
		if (element.getName().equals(XMLTag.XML_TAG_ENUM)){
			Env subEnv = newSubEnv();
			Enum enu = (Enum)(ModelElement.parseXML(subEnv, element));
			this.addEnum(enu);
		}
		else if (element.getName().equals(XMLTag.XML_TAG_NAME)){
			return ModelElement.MODELELEMENT_OK;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		

		return ModelElement.MODELELEMENT_OK;
	}

	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		if (null == statement){
			return ModelElement.MODELELEMENT_FAIL;
		}
		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_ENUM)){
			Env subEnv = newSubEnv();
			Enum enu = (Enum)(ModelElement.parseYANG(subEnv, statement));
			this.addEnum(enu);
		}
		else {
			return MODELELEMENT_FAIL;
		}
		

		return ModelElement.MODELELEMENT_OK;
	}
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return ModelElement.MODELELEMENT_FAIL;
		}
		if (statement.getName().equals(YangStatement.YANG_STATEMENT_ENUM)){
			Env subEnv = newSubEnv();
			Enum enu = (Enum)(ModelElement.parseYin(subEnv, statement));
			this.addEnum(enu);
		}
		else {
			return MODELELEMENT_FAIL;
		}
		

		return ModelElement.MODELELEMENT_OK;
	}
	public boolean checkValue(String value){
		Type base = null;
		if (null == value){
			return false;
		}
		if (this.isDerivedType()){
			base = this.getBaseType();
		}
		else {
			base = this;
		}
		if(null == ((Enumeration)base).getEnum(value)){
			return false;
		}
		return true;
	}
}
